//
//  String+PPKit.swift
//  PPKit
//
//  Created by xiaopin on 16/8/31.
//  Copyright © 2016年 pinguo. All rights reserved.
//  https://gitee.com/xiaopindev/PPKit
//  https://github.com/xiaopindev
//  https://xiaopin.cnblogs.com

import Foundation
import UIKit
import CommonCrypto

public extension String {
    
    /*
     索引功能使用方法：
     var str = "hangge.com"
     print(str[7,3])
     print(str[7])
     
     str[7,3] = "COM"
     str[0] = "H"
     
     print(str[0,10])
     
     结果：
     com
     c
     Hangge.COM
     */
    subscript(start:Int, length:Int) -> String {
        get{
            let index1 = self.index(self.startIndex, offsetBy: start)
            let index2 = self.index(index1, offsetBy: length)
            let range = Range(uncheckedBounds: (lower: index1, upper: index2))
            return String(self[range])
        }
        set{
            let tmp = self
            var s = ""
            var e = ""
            for (idx, item) in tmp.enumerated() {
                if(idx < start)
                {
                    s += "\(item)"
                }
                if(idx >= start + length)
                {
                    e += "\(item)"
                }
            }
            self = s + newValue + e
        }
    }
    subscript(index:Int) -> String {
        get{
            return String(self[self.index(self.startIndex, offsetBy: index)])
        }
        set{
            let tmp = self
            self = ""
            for (idx, item) in tmp.enumerated() {
                if idx == index {
                    self += "\(newValue)"
                }else{
                    self += "\(item)"
                }
            }
        }
    }
    
    //MARK: 属性
    var length: Int {
        return self.count
    }
    
    /// 得到当前语言字符串
    var localized:String{
        return pp_localizedString(self)
    }
    
    /// MD5加密字符串（全部转大写字符）
    var MD5:String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = CUnsignedInt(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<UInt8>.allocate(capacity: 16)
        CC_MD5(str!, strLen, result)
        let hash = NSMutableString()
        for i in 0 ..< digestLen {
            hash.appendFormat("%02x", result[i])
        }
        free(result)
        return String(format: hash as String).uppercased()
    }
    
    /// MD5加密字符串（全部转小写字符）
    var md5:String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = CUnsignedInt(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<UInt8>.allocate(capacity: 16)
        CC_MD5(str!, strLen, result)
        let hash = NSMutableString()
        for i in 0 ..< digestLen {
            hash.appendFormat("%02x", result[i])
        }
        free(result)
        return String(format: hash as String).lowercased()
    }
    
    // MARK: json转字典
    var toDict: [String: Any?]? {
        
        var json = self
        
        if json.contains("\\") {
            json = json.components(separatedBy: "\\").joined()
        }
        
        if let data = json.data(using: .utf8) {
            do {
                let array = try JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.init(rawValue: 0)]) as? Array<[String: Any]>
                
                guard let dict = array?.first else {
                    return nil
                }
                return dict
            } catch {
            }
        }
        return nil
    }
    
    // MARK: json转数组
    var toArray: [[String: Any]]? {
        
        var json = self
        
        if json.contains("\\") {
            json = json.components(separatedBy: "\\").joined()
        }
        
        if let data = json.data(using: .utf8) {
            do {
                let array = try JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.init(rawValue: 0)]) as? [[String: Any]]
                
                return array
            } catch {
            }
        }
        return nil
    }
    
    //MARK: 类方法
    /**
     转换类名字符串格式
     
     :param: aClass <#aClass description#>
     
     :returns: <#return value description#>
     */
    static func className(_ aClass: AnyClass) -> String {
        return NSStringFromClass(aClass).components(separatedBy: ".").last!
    }
    
    /**
     判断是否为空
     
     :param: str <#str description#>
     
     :returns: <#return value description#>
     */
    static func isNullOrEmpty(_ str: String?) -> Bool {
        if str == nil {
            return true
        }
        let s = str?.trimmingCharacters(in: .whitespacesAndNewlines)
        if s?.isEmpty ?? true {
            return true
        }
        return false
    }
    
    /// 如果不为空
    /// - Parameter str: <#str description#>
    /// - Returns: <#description#>
    static func isNotNullOrEmpty(_ str: String?) -> Bool {
        if let s = str {
            let temp = s.trimmingCharacters(in: .whitespacesAndNewlines)
            if !temp.isEmpty{
                return true
            }
        }
        return false
    }
    
    /**
     根据文件字节自动转换成KB,MB,GB等字符串
     
     :param: size bytes总字节
     
     :returns: <#return value description#>
     */
    static func fileSizeFormat(_ totalBytes:UInt) -> String {
        var size = totalBytes
        var extIndex = 0
        let array = ["B","KB","MB","GB","TB","PB","EB","ZB","YB"]
        
        while size > 1024 && extIndex < array.count {
            size = size / 1024
            extIndex = extIndex + 1
        }
        
        if size == 0 {
            return ""
        }
        
        return String(format: "%.2f %@", Float(size), array[extIndex])
    }
    
    //MARK: 实例方法
    func isNullOrEmpty() -> Bool {
        let str = self.trimmingCharacters(in: .whitespacesAndNewlines)

        if str.isEmpty {
            return true
        }
        return false
    }
    
    func isNotNullOrEmpty() -> Bool {
        let temp = self.trimmingCharacters(in: .whitespacesAndNewlines)
        if !temp.isEmpty{
            return true
        }
        return false
    }
    /// 判断是否含有某个字符串
    func contains(find: String) -> Bool {
        return self.range(of: find) != nil
    }
    
    /// 判断是否含有某个字符串 忽略大小写
    func containsIgnoringCase(find: String) -> Bool {
        return self.range(of: find, options: .caseInsensitive) != nil
    }
    
    /// 字符串截取
    ///
    /// - Parameters:
    ///   - start: <#start description#>
    ///   - length: <#length description#>
    /// - Returns: <#return value description#>
    func subString(start:Int, length:Int = -1) -> String {
        var len = length
        if len == -1 {
            len = self.count - start
        }
        let st = self.index(startIndex, offsetBy:start)
        let en = self.index(st, offsetBy:len)
        return String(self[st ..< en])
    }

    /// 获取字符串NSRange
    /// - Parameter string: <#string description#>
    /// - Returns: <#description#>
    func nsRange(of string:String) -> NSRange{
        if let range = self.range(of: string) {
            return NSRange(range, in: self)
        }
        return NSRange(location: 0, length: 0)
    }
    
    /// 匹配字符串中第一个对应字符NSRange
    func exMatchStrRange(_ matchStr: String) -> NSRange {
        var selfStr = self as NSString
        var withStr = Array(repeating: "X", count: (matchStr as NSString).length).joined(separator: "") //辅助字符串
        if matchStr == withStr { withStr = withStr.lowercased() } //临时处理辅助字符串差错
        var allRange = [NSRange]()
        while selfStr.range(of: matchStr).location != NSNotFound {
            let range = selfStr.range(of: matchStr)
            allRange.append(NSRange(location: range.location,length: range.length))
            selfStr = selfStr.replacingCharacters(in: NSMakeRange(range.location, range.length), with: withStr) as NSString
        }
        guard let firstRange = allRange.first else {
            print("请检查NSRange的参数❌")
            return NSRange(location: 0, length: 0)
        }
        return firstRange
    }
    
    /// 匹配字符串中 "所有" 对应字符NSRange
    func exMatchStrAllRange(_ matchStr: String) -> [NSRange] {
        var selfStr = self as NSString
        var withStr = Array(repeating: "X", count: (matchStr as NSString).length).joined(separator: "") //辅助字符串
        if matchStr == withStr { withStr = withStr.lowercased() } //临时处理辅助字符串差错
        var allRange = [NSRange]()
        while selfStr.range(of: matchStr).location != NSNotFound {
            let range = selfStr.range(of: matchStr)
            allRange.append(NSRange(location: range.location,length: range.length))
            selfStr = selfStr.replacingCharacters(in: NSMakeRange(range.location, range.length), with: withStr) as NSString
        }
        return allRange
    }
    
    /// 字符串转富文本字符串
    /// - Parameters:
    ///   - lineSpacing: 上下间距,默认5
    ///   - font: 字体，默认苹果14，常规体
    ///   - aligent: 对齐，默认左对齐
    /// - Returns: <#description#>
    func str2AttrString(lineSpacing: CGFloat = 5, font: UIFont = UIFont.systemFont(ofSize: 14, weight: .regular), aligent: NSTextAlignment = .left) -> NSAttributedString {

        let paraStyle = NSMutableParagraphStyle()
        paraStyle.lineBreakMode = .byCharWrapping
        paraStyle.alignment  = aligent
        paraStyle.lineSpacing = lineSpacing

        paraStyle.hyphenationFactor = 0.0
        paraStyle.firstLineHeadIndent = 0.0
        paraStyle.paragraphSpacingBefore = 0.0
        paraStyle.headIndent = 0
        paraStyle.tailIndent = 0

        //将行间距设置为28
        let attributes = [
            NSAttributedString.Key.font:font,
            NSAttributedString.Key.paragraphStyle: paraStyle
        ]
        return  NSAttributedString(string:self , attributes: attributes)
    }
    
    //MARK: 表单校验
    ///判断是否是数字
    func isValid(_ format:String = "^[0-9]+$") -> Bool {
        let predicate = NSPredicate(format: "SELF MATCHES %@", format)
        return predicate.evaluate(with: self)
    }
    
    ///判断是否是手机号
    func isValidMobile() -> Bool {
        //手机号以13，15,166，18开头，八个 \d 数字字符
        let patternString = "^1(3|5|6|7|8|9)\\d{9}$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }

    ///判断是否是固定电话
    func isValidTelephone() -> Bool {
        let patternString = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }

    ///判断是不是符合要求的用户名
    func isValidUserName() -> Bool {
        // 字母和数字
        let patternString = "^[A-Za-z0-9]{6,20}+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    ///判断是不是符合要求的密码
    func isValidPassword() -> Bool {
        // 验证密码是 6 - 16 位字母或数字
        let patternString = "^[0-9A-Za-z]{6,16}$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    ///判断是不是符合要求的昵称:中文、英文、数字
    func isValidNickName() -> Bool {
        let patternString = "^[a-zA-Z0-9\\u4E00-\\u9FA5]+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    ///判断是不是符合要求的名字
    func isValidName() -> Bool {
        let patternString = "^[\\u4e00-\\u9fa5]{1,5}$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    /// 是否是邮箱
    func isValidEmail() -> Bool {
        let patternString = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    /// 判断是否是URL字符串
    func isValidURL() -> Bool {
        // 验证字符串是否是URL
        let patternString = "^(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", patternString)
        return predicate.evaluate(with: self)
    }
    
    /// 判断是否全部英文
    func isEnglish() -> Bool{
        let match: String = "^[A-Za-z]+$"
        let predicate = NSPredicate(format: "SELF matches %@", match)
        return predicate.evaluate(with: self)
    }
    ///判断中英文
    func istextEnglishOrchinaHAN(_ format:String = "^[\\u4e00-\\u9fa5a-zA-Z]+$") -> Bool{
        let predicate = NSPredicate(format: "SELF MATCHES %@", format)
        return predicate.evaluate(with: self)
    }
    
    /// 判断是否包含中文
    var includeChinese:Bool {
        for c in self {
            if ("\u{4E00}" <= c  && c <= "\u{9FA5}") {
                return true
            }
        }
        return false
    }
    
    /// 编码转换，当URL的路径包含中文时，需要做编码转换
    func addPE() -> String {
        let a = self.addingPercentEncoding(withAllowedCharacters:.urlQueryAllowed)
        return a ?? "1"
    }
    
    /// 追加URL参数
    /// - Parameter param: 参数，如userId=666,会自动往URL后面追加 不用管?和&
    /// - Returns: 返回加入后的URL字符串
    func appendURLParam(_ param:String) -> String{
        if self.isValidURL() == false {
            return self
        }
        var urlStr = self
        var queryStr = ""
        let index = self.nsRange(of: "?").location
        if index > 0 {
            urlStr = self[0,index]
            queryStr = self.subString(start: index)
            queryStr += "&\(param)"
        }else{
            queryStr += "?\(param)"
        }
        return urlStr + queryStr
    }
    
    func getIntFromString() -> Int {
        let scanner = Scanner(string: self)
        scanner.scanUpToCharacters(from: CharacterSet.decimalDigits, into: nil)
        var number:Int = 0
        scanner.scanInt(&number)
        return number
    }
}

extension String {
    ///BankCard插空格
    mutating func addBlank(per: Int = 4) -> String {
           let spaceCount = Int(count/per)
           for i in 1 ..< spaceCount + 1 {
               // 字符串插入空格
               self.insert(contentsOf: " ", at: self.index(self.startIndex, offsetBy: per*i + i-1))
           }
           return self
       }
    var toDic: [String: Any]? {
        guard let strData = self.data(using:.utf8) else { return nil }
        let data = try? JSONSerialization.jsonObject(with:strData,options:.mutableContainers)
        return data as? [String: Any]
    }
    
    var toArr: [Any]? {
        guard let strData = self.data(using:.utf8) else { return nil }
        let data = try? JSONSerialization.jsonObject(with:strData,options:.mutableContainers)
        return data as? [Any]
    }
    
    func isPureFloat() -> Bool {
        let scan: Scanner = Scanner(string:self)
        var val:Float = 0
        return scan.scanFloat(&val) && scan.isAtEnd
    }
    
    func isPureInt() -> Bool {
        let scan: Scanner = Scanner(string:self)
        var val:Int = 0
        return scan.scanInt(&val) && scan.isAtEnd
    }
}

extension Data {
    var html2AttributedString: NSAttributedString? {
        do {
            return try NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            print("error:", error)
            return  nil
        }
    }
    var html2String: String { html2AttributedString?.string ?? "" }
}

extension StringProtocol {
    var html2AttributedString: NSAttributedString? {
        Data(utf8).html2AttributedString
    }
    var html2String: String {
        html2AttributedString?.string ?? ""
    }
}
